﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using Puli.IndustrialCommunication.VariableAttributes;
using TwinCAT.Ads;

namespace Puli.IndustrialCommunication.Ads
{
    /// <summary>
    /// 基于TwinCAT.Ads通讯库的ORM框架。实现了以下功能：1）可以映射实体类，对实体类进行读写；2）还可对单个变量，通过变量名进行读写
    /// </summary>
    class AdsCommunication
    {

        #region Ads通讯相关参数
        public string NetId { get; set; }
        public int Port { get; set; }
        public AdsClient AdsClient  { get; init; }
        #endregion
        /// <summary>
        /// 存储查询过Handle的所有实体类的变量名和handle的键值对
        /// </summary>
        private Dictionary<string, uint> _variableHandlePairs { get; set; }
        //初始化
        public AdsCommunication()
        {
            AdsClient = new AdsClient();
            _variableHandlePairs = new Dictionary<string, uint>();
        }
        #region 连接PLC
        public bool Connect()
        {
            //清除所有的已存在的handle
            _variableHandlePairs.Clear();
            //判断NetId是否为空，或者Port是否为0
            if (NetId is null||Port==0)
            {
                return false;
            }
            else
            {
                try
                {
                    AdsClient.Connect(NetId, Port);
                    return true;
                }
                catch (Exception)
                {
                   throw;
                }
            }

        }
        #endregion
        #region 获取Handle
        /// <summary>
        /// 根据变量名称获取handle
        /// </summary>
        /// <param name="variableName"></param>
        public void GetVariableHandle(String variableName)
        {
            try
            {
                uint handle = AdsClient.CreateVariableHandle(variableName);
                if (!_variableHandlePairs.ContainsKey(variableName))
                {
                    _variableHandlePairs.Add(variableName, handle);//将变量名-handle写入字典中存放
                }
            }
            catch (Exception)
            {

                throw;
            }
        }
        /// <summary>
        /// 通过反射获取变量的handle
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="adsClient"></param>
        /// <param name="t"></param>
        public void GetVariableHandle<T>() where T : new()//约束T类型有构造函数
        {
            try
            {
                PropertyInfo[] propertyInfos = typeof(T).GetProperties();//获取所有公共属性
                foreach (var f in propertyInfos)
                {
                    if (f.CustomAttributes.Count() > 0)
                    {
                        var attrs = f.GetCustomAttribute<VariableAttribute>();//获取含有AdsVariableAttribute类型特性的所有特性
                        if (attrs != null)
                        {
                            string variableName = attrs.VariableName;//获取变量名称
                            uint handle = AdsClient.CreateVariableHandle(variableName);
                            if (!_variableHandlePairs.ContainsKey(variableName))
                            {
                                _variableHandlePairs.Add(variableName, handle);//将变量名-handle写入字典中存放
                            }
                        }
                    }
                }
            }
            catch (Exception)
            {

                throw ;
            }

        }
        #endregion

        #region 读取数据

        /// <summary>
        /// 根据变量名获取变量值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        public T GetValue<T>(string variableName) 
        {
            try
            {
                if (!_variableHandlePairs.ContainsKey(variableName))//判断handle集合内是否包含该变量
                {
                    GetVariableHandle(variableName);
                    _variableHandlePairs.TryGetValue(variableName, out uint handle);
                    return AdsClient.ReadAny<T>(handle);
                }
                else
                {
                    _variableHandlePairs.TryGetValue(variableName, out uint handle);
                    return AdsClient.ReadAny<T>(handle);
                }
            }
            catch (Exception)
            {

                throw;
            }
        }
        /// <summary>
        /// 根据实体类型获取变量值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        public void GetValue<T>(ref T t) where T : new()//约束T类型有构造函数
        {
            try
            {
                //获取一个类中包含 VariableAttribute的所有属性 
                PropertyInfo[] propertyInfos = typeof(T).GetProperties().Where(p => p.GetCustomAttributes<VariableAttribute>().Count() > 0).ToArray();               
                foreach (var item in propertyInfos)
                {
                    //获取特性值
                    var attrs = item.GetCustomAttributes<VariableAttribute>().FirstOrDefault();
                    //获取变量名称
                    string variableName = attrs.VariableName;
                    if (!_variableHandlePairs.ContainsKey(variableName))//判断handle集合内是否包含该变量
                    {
                        GetVariableHandle(variableName);
                        _variableHandlePairs.TryGetValue(variableName, out uint handle);
                        var value =AdsClient.ReadAny(handle, item.GetType());
                        item.SetValue(t, value);
                    }
                    else
                    {
                        _variableHandlePairs.TryGetValue(variableName, out uint handle);
                        var value = AdsClient.ReadAny(handle,item.GetType());
                        item.SetValue(t, value);
                    }
                }
            }
            catch (Exception)
            {
                throw;
            }

        }
        #endregion
        #region 写入数据
        /// <summary>
        /// 根据变量名写入数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        public bool SetValue<T>(string variableName,T value) 
        {
            try
            {
                if (!_variableHandlePairs.ContainsKey(variableName))//判断handle集合内是否包含该变量
                {
                    GetVariableHandle(variableName);
                    _variableHandlePairs.TryGetValue(variableName, out uint handle);
                    AdsClient.WriteAny(handle,value);
                    return true;
                }
                else
                {
                    _variableHandlePairs.TryGetValue(variableName, out uint handle);
                    AdsClient.WriteAny(handle, value);
                    return true;
                }
            }
            catch (Exception)
            {
                throw;
            }
        }
        /// <summary>
        /// 根据实体类写入数据
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="adsClient"></param>
        /// <param name="t"></param>
        public void SetVariablesHandlesValue<T>(T t) where T : class, new()//约束T类型有构造函数
        {
            try
            {
                //获取一个类中包含 VariableAttribute的所有属性 
                PropertyInfo[] propertyInfos = typeof(T).GetProperties().Where(p => p.GetCustomAttributes<VariableAttribute>().Count() > 0).ToArray();
                foreach (var item in propertyInfos)
                {
                    //获取特性值
                    var attrs = item.GetCustomAttributes<VariableAttribute>().FirstOrDefault();
                    //获取变量名称
                    string variableName = attrs.VariableName;
                    if (!_variableHandlePairs.ContainsKey(variableName))//判断handle集合内是否包含该变量
                    {
                        GetVariableHandle(variableName);
                        _variableHandlePairs.TryGetValue(variableName, out uint handle);
                        var value =item.GetValue(t);
                        AdsClient.WriteAny(handle, value);
                    }
                    else
                    {
                        _variableHandlePairs.TryGetValue(variableName, out uint handle);
                        var value = item.GetValue(t);
                        AdsClient.WriteAny(handle, value);
                    }
                }
            }
            catch (Exception)
            {

                throw;
            }
        }
        #endregion

    }
}
